package p.ithorns.sample.module;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.web.servlet.RegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.ResolvableType;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import p.ithorns.sample.ModulePathConfig;

import java.lang.reflect.Constructor;
import java.util.Set;

/**
 * ServletRegistrationRegistrar
 * <p>
 * ServletRegistrationBean的注入参考{@link RegistrationBean#onStartup}
 *
 * @author ithorns
 * @version 1.0
 * @date 2021/10/26 13:56
 */
@Slf4j
public class ModuleInterfaceRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        log.info("Start to load ServletRegistrationRegistrar...");
        ModuleInterfaceScanner scanner = new ModuleInterfaceScanner(registry);
        Set<BeanDefinition> configs = scanner.scan("com.test");

        for (BeanDefinition beanDef : configs) {
            ResolvableType resolvableType = beanDef.getResolvableType();
            // 这里可以处理MyBean类型的bean定义
            String beanClassName = beanDef.getBeanClassName();
            try {

                ModulePathConfig module = getBean(beanClassName);
                // 如果需要单独的DispatcherServlet, 则需要先注入
                // registerServlet(registry, moduleName);

                registerServletBean(registry, module);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    private void registerServletBean(BeanDefinitionRegistry registry, ModulePathConfig module) {
        String name = module.getModuleName();
        // 重要：这里path一定要跟“/*”后缀
        String path = module.getPath();
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                .genericBeanDefinition(ServletRegistrationBean.class)
                // 使用引用来实现后置注入
                .addConstructorArgReference("dispatcherServlet")
                .addConstructorArgValue(path)
                .addPropertyValue("name", name + "Servlet")
                // 这个通过名字注入对象的？？？ 没搞清楚
                .addPropertyValue("multipartConfig", "multipartConfig")
                .getBeanDefinition();
        registry.registerBeanDefinition(name + "Registration", beanDefinition);
    }

    /**
     * 注入DispatcherServlet
     *
     * @param registry   -
     * @param moduleName -
     */
    private void registerServlet(BeanDefinitionRegistry registry, String moduleName) {
        String servletName = moduleName + "Servlet";
        BeanDefinitionBuilder dispatcherBuilder = BeanDefinitionBuilder
                .genericBeanDefinition(DispatcherServlet.class)
                .addPropertyValue("contextClass", AnnotationConfigWebApplicationContext.class);
        registry.registerBeanDefinition(servletName, dispatcherBuilder.getBeanDefinition());
    }

    private ModulePathConfig getBean(String beanClassName) {
        try {
            Class<?> aClass = Class.forName(beanClassName);
            Constructor<?>[] constructors = aClass.getDeclaredConstructors();
            Object bean = constructors[0].newInstance(null);
            return (ModulePathConfig) bean;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}